package com.suning.sawp.service.util;

import java.io.IOException;
import java.io.StringReader;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.exception.ZkNoNodeException;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;

/**
 * 〈配置资源文件读取工具类〉<br>
 * 〈zookeeper统一配置〉
 * 
 * @author 12061748
 * @see [相关类/方法]（可选）
 * @since [产品/模块版本] （可选）
 */
public class ConfigUtil implements InitializingBean {
    private static final Logger LOGGER = LoggerFactory.getLogger(ConfigUtil.class);

    private static final String PREFIX = "/";

    // 项目配置文件约定统一在config下面
    private static final String PREFIX_CONFIG = "config";

    private static final int CON_ZOOKEEPER_TIMEOUT = 20000;

    // zookeeper地址
    private String zookeeperAddress;

    private ZkClient zkClient;

    // 接收数据map
    private static Map<String, Map<String, String>> properMap = new ConcurrentHashMap<String, Map<String, String>>();

    /**
     * 
     * 功能描述: <br>
     * 〈功能详细描述〉
     *
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    public ZkClient getZkClient() {
        return zkClient;
    }

    /**
     * 
     * 功能描述: <br>
     * 〈功能详细描述〉
     *
     * @param zookeeperAddress
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    public void setZookeeperAddress(String zookeeperAddress) {
        this.zookeeperAddress = zookeeperAddress;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        initZkClient();
    }

    /**
     * 
     * 功能描述: 初始化zookeeper服务 <br>
     * 〈功能详细描述〉
     * 
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    public void initZkClient() {
        if (zkClient != null) {
            zkClient.close();
        }
        try {
            zkClient = new ZkClient(zookeeperAddress, CON_ZOOKEEPER_TIMEOUT);
        } catch (Exception e) {
            LOGGER.error("init ZkClient find wrong.", e);
            return;
        }

        // 给特定项目节点绑定内容改变捕获的事件

        List<String> children = null;
        try {
            children = zkClient.getChildren(PREFIX + PREFIX_CONFIG);
        } catch (ZkNoNodeException e) {
            LOGGER.error("initZkClient ZkNoNodeException", e);
            zkClient.createPersistent(PREFIX + PREFIX_CONFIG);
            return;
        }
        // 防止zookeeper未配置导致服务出问题
        if (CollectionUtils.isNotEmpty(children)) {
            // 查询出所有根节点（即没有子节点的节点）
            Map<String, String> propertiesMap = getAllRootNode(children, PREFIX + PREFIX_CONFIG,
                    new HashMap<String, String>());

            // 遍历map监控所有根节点的数据变化
            for (Entry<String, String> entry : propertiesMap.entrySet()) {
                // 根节点
                String eachPath = entry.getKey();
                String dataVal = entry.getValue() == null ? "" : entry.getValue().toString();
                Map<String, String> map = parseString2Map(dataVal);
                if (MapUtils.isNotEmpty(map)) {
                    // 给properMap塞值
                    properMap.put(eachPath, map);
                    // 监控更节点数据的变化触发的事件
                    zkClientSubscribeDataChanges(eachPath);
                }
            }
        }
    }

    /**
     * 
     * 功能描述:查询出所有根节点（即没有子节点的节点） <br>
     * 〈功能详细描述〉
     * 
     * @param children
     * @param parentPath
     * @param rootMap
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    private Map<String, String> getAllRootNode(List<String> children, String parentPath, Map<String, String> rootMap) {

        for (String eachPath : children) {
            Boolean hasChildren = !zkClient.getChildren(parentPath + PREFIX + eachPath).isEmpty();
            if (hasChildren) {
                List<String> childrens = zkClient.getChildren(parentPath + PREFIX + eachPath);
                // 递归查询子节点
                rootMap = this.getAllRootNode(childrens, parentPath + PREFIX + eachPath, rootMap);
            } else {
                Object data = zkClient.readData(parentPath + PREFIX + eachPath);
                rootMap.put(parentPath + PREFIX + eachPath, data == null ? "" : data.toString());
            }
        }
        return rootMap;
    }

    /**
     * 
     * 功能描述: 监控节点data的改变<br>
     * 〈功能详细描述〉
     * 
     * @param path
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    private void zkClientSubscribeDataChanges(String path) {
        zkClient.subscribeDataChanges(path, new IZkDataListener() {
            @Override
            public void handleDataChange(String dataPath, Object data) throws Exception {
                if (null != data) {
                    Map<String, String> map = parseString2Map(data.toString());
                    if (MapUtils.isNotEmpty(map)) {
                        properMap.put(dataPath, map);
                    }
                }
            }
            @Override
            public void handleDataDeleted(String dataPath) throws Exception {

            }
        });
    }

    /**
     * 
     * 功能描述:json转化为map工具 <br>
     * 〈功能详细描述〉
     * 
     * @param jsonStr
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    private Map<String, String> parseString2Map(String str) {
        if (StringUtils.isEmpty(str)) {
            return null;
        }
        Map<String, String> map = new HashMap<String, String>();
        Properties p = new Properties();
        try {
            p.load(new StringReader(str));
            for (Entry<Object, Object> entry : p.entrySet()) {
                // 根节点
                String key = (String) entry.getKey();
                //
                String value = entry.getValue() == null ? "" : String.valueOf(entry.getValue());
                map.put(key, value);
            }
        } catch (IOException e) {
            LOGGER.error("", e);
        }
        return map;
    }

    /**
     * 
     * 功能描述:提供给外部资源调用获取zookeeper服务端资源文件的值 <br>
     * 〈功能详细描述〉
     * 
     * @param path
     * @param node
     * @param key
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    public static String getConfig(String path, String key) {
        Assert.notNull(path);
        Assert.notNull(key);
        if (null == properMap.get(path)) {
            return null;
        }
        return properMap.get(path).get(key);
    }

    /**
     * 
     * 功能描述: 如果path和key取不到值返回传递的默认值<br>
     * 〈功能详细描述〉
     * 
     * @param path
     * @param key
     * @param defaultValue
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    public static String getConfig(String path, String key, String defaultValue) {
        Assert.notNull(path);
        Assert.notNull(key);
        if (MapUtils.isEmpty(properMap.get(path)) || StringUtils.isEmpty(properMap.get(path).get(key))) {
            return defaultValue;
        }
        return properMap.get(path).get(key);
    }
    
    /**
     * 
     * 功能描述: 更新zookeeper节点值<br>
     * 〈功能详细描述〉
     *
     * @param path
     * @param key
     * @param newValue
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    public void updateConfig(String path, String key, String newValue) {
        Assert.notNull(path);
        Assert.notNull(key);
        Assert.notNull(newValue);
        zkClient.writeData(path, key + "=" + newValue);
        properMap.get(path).put(key, newValue);
    }
    
    /**
     * 
     * 功能描述: 如果path取不到值返回defaultMap<br>
     * 〈功能详细描述〉
     * 
     * @param path
     * @param defaultMap
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    public static Map<String,String> getConfigMap(String path,Map<String,String> defaultMap) {
        Assert.notNull(path);
        if (MapUtils.isEmpty(properMap.get(path))) {
            return defaultMap;
        }
        return properMap.get(path);
    }
}

